这是一个介绍 Android 平台日志系统的系列文章:
- Android 平台日志系统整体框架 (本文)
- logd 守护进程初始化过程
- 客户端写日志过程分析
- logd 写日志过程分析
- 冗余日志处理过程分析
- logcat 读日志过程分析
- logd 读日志过程分析
- 内核日志处理分析
- SeLinux 日志处理分析
本文基于 AOSP android-10.0.0_r41 版本讲解
# 1. 日志的类别与基本使用
在 Android 系统中,常见的日志有 5 类,main、radio、events、system、crash:
main 日志
main 日志是应用层 App 唯一可用的日志类型。
在 Java 层提供了 android.util.Log
接口来写 main 日志:
// 写 VERBOSE 等级的日志
Log.v("YOUR TAG", "Message body");
// 写 DEBUG 等级的日志
Log.d("YOUR TAG", "Message body");
// 写 INFO 等级的日志
Log.i("YOUR TAG", "Message body");
// 写 WARN 等级的日志
Log.w("YOUR TAG", "Message body");
// 写 ERROR 等级的日志
Log.e("YOUR TAG", "Message body");
2
3
4
5
6
7
8
9
10
android.util.Log
接口提供了 5 个方法来写 5 个级别(Level)的日志,日志级别分别为 VERBOSE、DEBUG、INFO、WARN、ERROR、ASSERT,日志级别依次提升:
- VERBOSE 级别日志:开发调试中的一些详细信息,仅在开发中使用,不可在发布产品中输出,不是很常见,包含诸如方法名,变量值之类的信息
- DEBUG 级别日志:用于调试的信息,可以在发布产品中关闭,比较常见,开发中经常选择输出此种级别的日志,有时在 beta 版应用中出现
- INFO 级别日志:该等级日志显示运行状态信息,可在产品出现问题时提供帮助,从该级别开始的日志通常包含完整的文本信息和调试信息,是最常见的日志级别
- WARN 级别日志:运行出现异常即将发生错误或表明已发生非致命性错误,该级别日志通常显示出执行过程中的意外情况,例如将 try-catch 语句块中的异常打印堆栈轨迹之后可输出此种级别日志
- ERROR 级别日志:已经出现可影响运行的错误,比如应用 crash 时输出的日志
在 native 层提供了 __android_log_print
函数来写 main 日志。习惯上,会自定义一些宏来简化 __android_log_print
函数的使用:
#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__)
2
3
4
5
与 Java 层类似,在 Natvie 层也有 5 个级别(Level)的日志输出函数。
radio 日志
radio 日志主要用于记录无线装置/电话相关系统应用的日志,可以调用 android.telephony.Rlog
打印日志。
events 日志
events 日志是用来诊断系统问题的。在应用框架提供了 android.util.EventLog
接口写日志,native 层提供了宏 LOG_EVENT_INT、LOG_EVENT_LONG、LOG_EVENT_FLOAT、LOG_EVENT_STRING
用来写入 events 类型日志。
system 日志
system 日志主要用于记录系统应用的日志信息,在 Java 层提供了 android.util.SLog
接口写入日志, native 层提供了 SLOGV、SLOGD、SLOGI、SLOGW、SLOGE 等宏用来写入 system 类型的日志。
crash 日志
- crash 日志通常是程序 crash 时,记录的日志类型, Java 层使用
Log.println()
(第一个参数设置为 LOG_ID_CRASH)打印 crash 类型的日志
了解了如何写入日志,接下来我们看看如何读取日志。
通常使用 logcat 命令查看日志:
logcat -b + 参数
可选的参数主要有:
- all:查看所有缓冲区日志
- default:查看 main、system、crash 三个类型日志信息
- main:查看 main 类型日志
- radio:查看 radio 类型日志
- events:查看 events 类型日志
- system:查看 system 类型日志
- crash:查看 crash 类型日志
比如:
logcat -b main
# 用来查看 main 和 system 类型日志
logcat -b main,system
logcat -b all #查看所有日志
2
3
4
# 2.Kernel 层的日志
Kernel 层通常使用 printk
函数来记录日志,具体用法如下:
// KERN_INFO为日志级别,"Message body\n"则为日志信息
printk(KERN_INFO "Message body\n");
2
kernel 日志级别分别是:KERN_EMERG,KERN_ALERT,KERN_CRIT,KERN_ERR,KERN_WARNING,KERN_NOTICE,KERN_INFO,KERN_DEBUG
这些级别定义在内核源码中的 kern_levels.h 文件中:
#define KERN_EMERGKERN_SOH "0"/* system is unusable */
#define KERN_ALERTKERN_SOH "1"/* action must be taken immediately */
#define KERN_CRITKERN_SOH "2"/* critical conditions */
#define KERN_ERRKERN_SOH "3"/* error conditions */
#define KERN_WARNINGKERN_SOH "4"/* warning conditions */
#define KERN_NOTICEKERN_SOH "5"/* normal but significant condition */
#define KERN_INFOKERN_SOH "6"/* informational */
#define KERN_DEBUGKERN_SOH "7"/* debug-level messages */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Kernel 日志输出到 /proc/kmsg 文件,用户可以通过读取这个文件来获取 kernel 日志:
cat /proc/kmsg
# 3. 日志系统整体框架
图片来自Android logd日志简介及典型案例分析 (opens new window),稍作修改。
系统启动时,init 进程会启动 logd 进程,logd 进程会启动多个子线程,我们主要关注其中的三个:
- LogListener,启动一个 Unix Domain Socket 服务端,socket 服务端对应的文件是
/dev/socket/logdw
,用于接受 App 发送的 socket 远程请求,向 LogBuffer 写入日志 - LogReader,启动一个 Unix Domain Socket 服务端,socket 服务端对应的文件是
/dev/socket/logdr
,用于接受 logcat 命令发送的 socket 远程请求,从 LogBuffer 中读出日志 - CommandListener,启动一个 Unix Domain Socket 服务端,socket 服务端对应的文件是
/dev/socket/logd
,用于接受 logcat 命令发送的 socket 远程请求,向 LogBuffer 发送管理命令
App 端写入日志时,最终都是调用到 __android_log_write
函数,通过 socket 远程调用,向 LogListener 发送消息,LogListener 读出 socket 消息,在写入 LogBuffer中
调用 logcat 命令时,最终都是调用到 __android_logger_read
和 SendLogdControlMessage
函数,前者通过 socket 远程调用,向 LogReader 发送消息,LogReader 读出 socket 消息,接着从 LogBuffer 中读出日志信息;后者通过 socket 远程调用,向 CommandListener 发送消息,LogReader 读出 socket 消息,接着向 LogBuffer 发送管理命令
# 参考资料
- Android Logd框架梳理 (opens new window)
- Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路] (opens new window)
- 深入理解安卓日志系统(logcat / liblog / logd) (opens new window)
- Android中 logd 详解 (opens new window)
- Android logd日志简介及典型案例分析 (opens new window)
- Android 10 根文件系统和编译系统(六):log系统和logcat命令 (opens new window)
- Android P 源码分析 4 - logd 的初始化 (opens new window)
- Android log 机制 - logd 如何接收 log 数据(下) (opens new window)